/*
 *
 *  Copyright (C) 2016-2022, Open Connections GmbH
 *  All rights reserved.  See COPYRIGHT file for details.
 *
 *  This software and supporting documentation are maintained by
 *
 *    OFFIS e.V.
 *    R&D Division Health
 *    Escherweg 2
 *    D-26121 Oldenburg, Germany
 *
 *
 *  Module:  dcmfg
 *
 *  Author:  Michael Onken
 *
 *  Purpose: Class for managing Image Data Type Functional Group
 *
 */

#include "dcmtk/config/osconfig.h"

#include "dcmtk/dcmdata/dcdeftag.h"
#include "dcmtk/dcmfg/fgimagedatatype.h"
#include "dcmtk/dcmfg/fgtypes.h"
#include "dcmtk/dcmiod/iodutil.h"

// Constructor
FGImageDataType::FGImageDataType()
    : FGBase(DcmFGTypes::EFG_IMAGEDATATYPE)
    , m_DataType(DCM_DataType)
    , m_AliasedDataType(DCM_AliasedDataType)
    , m_ZeroVelocityPixelValueUS(DCM_ZeroVelocityPixelValue)
    , m_ZeroVelocityPixelValueSS(DCM_ZeroVelocityPixelValue)
{
}

FGImageDataType::~FGImageDataType()
{
    // nothing to do
}

FGBase* FGImageDataType::clone() const
{
    FGImageDataType* copy = new FGImageDataType();
    if (copy)
    {
        copy->m_DataType        = this->m_DataType;
        copy->m_AliasedDataType = this->m_AliasedDataType;
        if (!OFconst_cast(DcmSignedShort*, &this->m_ZeroVelocityPixelValueSS)->isEmpty())
        {
            copy->m_ZeroVelocityPixelValueSS = this->m_ZeroVelocityPixelValueSS;
        }
        else if (!OFconst_cast(DcmUnsignedShort*, &this->m_ZeroVelocityPixelValueUS)->isEmpty())
        {
            copy->m_ZeroVelocityPixelValueUS = this->m_ZeroVelocityPixelValueUS;
        }
        // both empty? nothing to do then
    }
    return copy;
}

void FGImageDataType::clearData()
{
    m_DataType.clear();
    m_AliasedDataType.clear();
    m_ZeroVelocityPixelValueSS.clear();
    m_ZeroVelocityPixelValueUS.clear();
}

OFCondition FGImageDataType::check() const
{
    // For now, checks in read() and write() are sufficient
    return EC_Normal;
}

// --- get() functionality ---

OFCondition FGImageDataType::getDataType(OFString& value, const long signed int pos)
{
    return DcmIODUtil::getStringValueFromElement(m_DataType, value, pos);
}

OFCondition FGImageDataType::getAliasedDataType(OFString& value, const long signed int pos)
{
    return DcmIODUtil::getStringValueFromElement(m_AliasedDataType, value, pos);
}

OFCondition FGImageDataType::getZeroVelocityPixelValue(Sint32& value, const unsigned long pos)
{
    OFCondition result;
    if (!m_ZeroVelocityPixelValueSS.isEmpty())
    {
        Sint16 val = 0;
        if ((result = m_ZeroVelocityPixelValueSS.getSint16(val, pos)).good())
        {
            value = val;
        }
    }
    else if (!m_ZeroVelocityPixelValueUS.isEmpty())
    {
        Uint16 val = 0;
        if ((result = m_ZeroVelocityPixelValueUS.getUint16(val, pos)).good())
        {
            value = val;
        }
    }
    else
        return EC_TagNotFound;

    return EC_Normal;
}

// --- set() functionality ---

OFCondition FGImageDataType::setDataType(const OFString& value, const OFBool checkValue)
{
    OFCondition result = (checkValue) ? DcmCodeString::checkStringValue(value, "1") : EC_Normal;
    if (result.good())
        result = m_DataType.putOFStringArray(value);
    return result;
}

OFCondition FGImageDataType::setAliasedDataType(const OFString& value, const OFBool checkValue)
{
    OFCondition result = (checkValue) ? DcmCodeString::checkStringValue(value, "1") : EC_Normal;
    if (result.good())
        result = m_AliasedDataType.putOFStringArray(value);
    return result;
}

OFCondition FGImageDataType::setZeroVelocityPixelValueSS(const Sint16 value)
{
    return m_ZeroVelocityPixelValueSS.putSint16(value);
}

OFCondition FGImageDataType::setZeroVelocityPixelValueUS(const Uint16 value)
{
    return m_ZeroVelocityPixelValueUS.putUint16(value);
}

// Read Frame Content Sequence from given item
OFCondition FGImageDataType::read(DcmItem& item)
{
    clearData();

    DcmItem* seqItem   = NULL;
    OFCondition result = getItemFromFGSequence(item, DCM_ImageDataTypeSequence, 0, seqItem);
    if (result.bad())
        return result;

    DcmElement* elem = NULL;
    DcmIODUtil::getAndCheckElementFromDataset(*seqItem, m_DataType, "1", "1", "ImageDataTypeMacro");
    DcmIODUtil::getAndCheckElementFromDataset(*seqItem, m_AliasedDataType, "1", "1", "ImageDataTypeMacro");
    // We do not know the VR of Zero Velocity Pixel Value (US or SS), so we cannot
    // use the regular getAndCheckElementFromDataset() method that implicitly gets
    // this information via the provided element. Instead, get element first and
    // and then check it in an extra step, afterwards access value depending on
    // the element's VR.
    seqItem->findAndGetElement(DCM_ZeroVelocityPixelValue, elem);
    DcmIODUtil::checkElementValue(elem, DCM_ZeroVelocityPixelValue, "1", "1C", EC_Normal, "ImageDataTypeMacro");
    if (elem)
    {
        if (elem->getVR() == EVR_SS)
        {
            m_ZeroVelocityPixelValueSS.copyFrom(*elem);
        }
        else if (elem->getVR() == EVR_US)
        {
            m_ZeroVelocityPixelValueUS.copyFrom(*elem);
        }
        else
        {
            Uint16 pixrep = 0;
            if (item.findAndGetUint16(DCM_PixelRepresentation, pixrep).good())
            {
                if (pixrep == 0)
                {
                    m_ZeroVelocityPixelValueUS.putUint16(0, 0);
                }
                else
                {
                    m_ZeroVelocityPixelValueSS.putSint16(1, 1);
                }
            }
            else
            {
                DCMFG_ERROR("Cannot read Zero Velocity Pixel Value");
            }
        }
    }
    return EC_Normal;
}

// Writes single Frame Content Sequence into given item
OFCondition FGImageDataType::write(DcmItem& item)
{
    DcmItem* seqItem   = NULL;
    OFCondition result = createNewFGSequence(item, DCM_ImageDataTypeSequence, 0, seqItem);
    if (result.bad())
        return result;

    // --- write frame content macro attributes ---
    DcmIODUtil::copyElementToDataset(result, *seqItem, m_DataType, "1", "1", "ImageDataTypeMacro");
    DcmIODUtil::copyElementToDataset(result, *seqItem, m_AliasedDataType, "1", "1", "ImageDataTypeMacro");
    if (!m_ZeroVelocityPixelValueSS.isEmpty())
    {
        DcmIODUtil::copyElementToDataset(result, *seqItem, m_ZeroVelocityPixelValueSS, "1", "1C", "ImageDataTypeMacro");
    }
    else if (!m_ZeroVelocityPixelValueUS.isEmpty())
    {
        DcmIODUtil::copyElementToDataset(result, *seqItem, m_ZeroVelocityPixelValueUS, "1", "1C", "ImageDataTypeMacro");
    }
    // Zero Velocity Pixel Value is required in case Data Type is TISSUE_VELOCITY,
    // FLOW_VELOCITY or DIRECTION_POWER.
    else
    {
        OFString val;
        m_DataType.getOFStringArray(val);
        if ((val == "TISSUE_VELOCITY") || (val == "FLOW_VELOCITY") || (val == "DIRECTION_POWER"))
        {
            DCMFG_ERROR("Missing value for Zero Velocity Pixel Value (required if Data Type has"
                        << "the value TISSUE_VELOCITY, FLOW_VELOCITY or DIRECTION_POWER");
            return FG_EC_InvalidData;
        }
    }

    return result;
}

int FGImageDataType::compare(const FGBase& rhs) const
{
    int result = FGBase::compare(rhs);
    if (result != 0)
        return result;

    const FGImageDataType* myRhs = OFstatic_cast(const FGImageDataType*, &rhs);
    if (!myRhs)
        return -1;

    // Compare all elements
    result = m_DataType.compare(myRhs->m_DataType);
    if (result == 0)
        result = m_AliasedDataType.compare(myRhs->m_AliasedDataType);
    if (result == 0)
        result = m_ZeroVelocityPixelValueSS.compare(myRhs->m_ZeroVelocityPixelValueSS);
    if (result == 0)
        result = m_ZeroVelocityPixelValueUS.compare(myRhs->m_ZeroVelocityPixelValueUS);

    return result;
}
